#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <execinfo.h>
#include <unistd.h>
#include <signal.h>
#include <setjmp.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ucontext.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <elf.h>

const char *symtype[]={"STT_NOTYPE","STT_OBJECT","STT_FUNC","STT_SECTION","STT_FILE","STT_COMMON","STT_TLS"};
const char *symbind[]={"STB_LOCAL","STB_GLOBAL","STB_WEAK"};
const char *symvisual[]={"STV_DEFAULT","STV_INTERNAL","STV_HIDDEN","STV_PROTECTED"};

jmp_buf env;// 在子进程中需要被声明
int fd = -1;
int fd_num = 0;
Elf64_Ehdr elfhdr[30];
Elf64_Shdr *sectionHeaders[30];
unsigned char **sectionsPtr[30];
//节名字符串表下标
unsigned int shstrsecInd[30] = {0};
//.bss节下标
unsigned int bsssecInd[30] = {0};

//.strtab节下标
unsigned int strtabInd[30] = {0};
//.symtab节下标
unsigned int symtabInd[30] = {0};

//.dynstr节下标
unsigned int dynstrInd[30] = {0};
//.dynsym节下标
unsigned int dynsymInd[30] = {0};

void getElfHeader()
{
    read(fd,elfhdr[fd_num].e_ident,EI_NIDENT);
    read(fd,&elfhdr[fd_num].e_type,sizeof(elfhdr[fd_num].e_type));
    read(fd,&elfhdr[fd_num].e_machine,sizeof(elfhdr[fd_num].e_machine));
    read(fd,&elfhdr[fd_num].e_version,sizeof(elfhdr[fd_num].e_version));
    read(fd,&elfhdr[fd_num].e_entry,sizeof(elfhdr[fd_num].e_entry));
    read(fd,&elfhdr[fd_num].e_phoff,sizeof(elfhdr[fd_num].e_phoff));
    read(fd,&elfhdr[fd_num].e_shoff,sizeof(elfhdr[fd_num].e_shoff));
    read(fd,&elfhdr[fd_num].e_flags,sizeof(elfhdr[fd_num].e_flags));
    read(fd,&elfhdr[fd_num].e_ehsize,sizeof(elfhdr[fd_num].e_ehsize));
    read(fd,&elfhdr[fd_num].e_phentsize,sizeof(elfhdr[fd_num].e_phentsize));
    read(fd,&elfhdr[fd_num].e_phnum,sizeof(elfhdr[fd_num].e_phnum));
    read(fd,&elfhdr[fd_num].e_shentsize,sizeof(elfhdr[fd_num].e_shentsize));
    read(fd,&elfhdr[fd_num].e_shnum,sizeof(elfhdr[fd_num].e_shnum));
    read(fd,&elfhdr[fd_num].e_shstrndx,sizeof(elfhdr[fd_num].e_shstrndx));
}
 
void getSectionHeaders()
{
    sectionHeaders[fd_num]=(Elf64_Shdr *)calloc(elfhdr[fd_num].e_shnum,sizeof(Elf64_Shdr));
    lseek(fd,elfhdr[fd_num].e_shoff,SEEK_SET);
    int i=0;
    for(i=0;i<elfhdr[fd_num].e_shnum;i++)
    {
        read(fd,&sectionHeaders[fd_num][i].sh_name,sizeof(sectionHeaders[fd_num][i].sh_name));
        read(fd,&sectionHeaders[fd_num][i].sh_type,sizeof(sectionHeaders[fd_num][i].sh_type));
        read(fd,&sectionHeaders[fd_num][i].sh_flags,sizeof(sectionHeaders[fd_num][i].sh_flags));
        read(fd,&sectionHeaders[fd_num][i].sh_addr,sizeof(sectionHeaders[fd_num][i].sh_addr));
        read(fd,&sectionHeaders[fd_num][i].sh_offset,sizeof(sectionHeaders[fd_num][i].sh_offset));
        read(fd,&sectionHeaders[fd_num][i].sh_size,sizeof(sectionHeaders[fd_num][i].sh_size));
        read(fd,&sectionHeaders[fd_num][i].sh_link,sizeof(sectionHeaders[fd_num][i].sh_link));
        read(fd,&sectionHeaders[fd_num][i].sh_info,sizeof(sectionHeaders[fd_num][i].sh_info));
        read(fd,&sectionHeaders[fd_num][i].sh_addralign,sizeof(sectionHeaders[fd_num][i].sh_addralign));
        read(fd,&sectionHeaders[fd_num][i].sh_entsize,sizeof(sectionHeaders[fd_num][i].sh_entsize));
    }
}
 
void getSections(void)
{
    int i=0;
    sectionsPtr[fd_num]=(unsigned char **)calloc(elfhdr[fd_num].e_shnum,sizeof(unsigned char *));
    for(i=0;i<elfhdr[fd_num].e_shnum;i++)
    {
        if(sectionsPtr[fd_num][i]!=NULL)
            free(sectionsPtr[fd_num][i]);
        sectionsPtr[fd_num][i]=(unsigned char *)calloc(sectionHeaders[fd_num][i].sh_size,1);
        if(sectionsPtr[fd_num][i]==NULL)
        {
            printf("i=%u\tsh_size=%lu\n",i,sectionHeaders[fd_num][i].sh_size);
            perror("calloc:");
            exit(1);
        }
        lseek(fd,sectionHeaders[fd_num][i].sh_offset,SEEK_SET);
        read(fd,sectionsPtr[fd_num][i],sectionHeaders[fd_num][i].sh_size);
    }
 
    for(i=0;i<elfhdr[fd_num].e_shnum;i++)
    {
        if(strcmp(sectionsPtr[fd_num][elfhdr[fd_num].e_shstrndx]+sectionHeaders[fd_num][i].sh_name,".strtab")==0)
        {
            strtabInd[fd_num]=i;
        }
        else if(strcmp(sectionsPtr[fd_num][elfhdr[fd_num].e_shstrndx]+sectionHeaders[fd_num][i].sh_name,".symtab")==0)
        {
            symtabInd[fd_num]=i;
        }
        else if(strcmp(sectionsPtr[fd_num][elfhdr[fd_num].e_shstrndx]+sectionHeaders[fd_num][i].sh_name,".dynstr")==0)
        {
            dynstrInd[fd_num]=i;
        }
        else if(strcmp(sectionsPtr[fd_num][elfhdr[fd_num].e_shstrndx]+sectionHeaders[fd_num][i].sh_name,".dynsym")==0)
        {
            dynsymInd[fd_num]=i;
        }
    }
}

void prtSymbols(unsigned long long int pc, int fd_index)
{
    unsigned int i, count;
    if(symtabInd[fd_index] != 0)
    {
        count = sectionHeaders[fd_index][symtabInd[fd_index]].sh_size/sectionHeaders[fd_index][symtabInd[fd_index]].sh_entsize;
        for(i=0; i < count; i++)
        {
            Elf64_Sym tmpsym;
            memcpy(&tmpsym.st_name,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24,4);
            memcpy(&tmpsym.st_info,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24+4,1);
            memcpy(&tmpsym.st_other,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24+5,1);
            memcpy(&tmpsym.st_shndx,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24+6,2);
            memcpy(&tmpsym.st_value,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24+8,8);
            memcpy(&tmpsym.st_size,sectionsPtr[fd_index][symtabInd[fd_index]]+i*24+16,8);

            unsigned long long int start = tmpsym.st_value;
            unsigned long long int end = start + tmpsym.st_size;
            if(start <= pc && pc <= end)
            {
                printf("ELF:%s-%llx\n",sectionsPtr[fd_index][strtabInd[fd_index]]+tmpsym.st_name, pc-start);
                break;
            }
        }
        // printf("test-%d-%d\n",count, i);
    }
    else
    {
        count = sectionHeaders[fd_index][dynsymInd[fd_index]].sh_size/sectionHeaders[fd_index][dynsymInd[fd_index]].sh_entsize;
        for(i=0; i < count; i++)
        {
            Elf64_Sym tmpsym;
            memcpy(&tmpsym.st_name,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24,4);
            memcpy(&tmpsym.st_info,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24+4,1);
            memcpy(&tmpsym.st_other,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24+5,1);
            memcpy(&tmpsym.st_shndx,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24+6,2);
            memcpy(&tmpsym.st_value,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24+8,8);
            memcpy(&tmpsym.st_size,sectionsPtr[fd_index][dynsymInd[fd_index]]+i*24+16,8);

            unsigned long long int start = tmpsym.st_value;
            unsigned long long int end = start + tmpsym.st_size;
            if(start <= pc && pc <= end)
            {
                printf("ELF:%s-%llx\n",sectionsPtr[fd_index][dynstrInd[fd_index]]+tmpsym.st_name, pc-start);
                break;
            }
        }
        // printf("test-%d-%d\n",count, i);
    }
}

static void SigSegvProc(int signo, siginfo_t *si, void *ucontext)
{
    printf("this is sig %d\n", signo);
    int i;

    printf("------show map(Command)------\n");
    char buf[64] = {0};
    sprintf(buf, "cat /proc/%d/maps", getpid());
    system(buf);
    printf("------show map(Command)------\n");

    printf("------show stack(C)------\n");
    void *buffer[1024];
    int n = backtrace(buffer, 1024);
    unsigned long long int func_back_trace;
    ucontext_t *u_error_context = (ucontext_t *)ucontext;
    char **symbols = backtrace_symbols(buffer, n);
    // backtrace输出函数栈帧
    printf("backtrace::%d\n", n);
    for (i = 0; i < n; i++)
    {
        printf("%s\n", symbols[i]);
    }
    printf("------show stack(C)------\n");

    // 错误现场输出
    printf("------show stack(my)------\n");

    printf("fault addr::%llx\n", u_error_context->uc_mcontext.fault_address);
    printf("sp::%llx\n", u_error_context->uc_mcontext.sp);
    printf("pc::%llx\n", u_error_context->uc_mcontext.pc);
    printf("pstate::%llx\n", u_error_context->uc_mcontext.pstate);
    // for (i = 0; i < 31; i++)
    // {
    //     printf("X%d::%llx\n", i, u_error_context->uc_mcontext.regs[i]);
    // }

    unsigned long long int lr, fp;
    fp = u_error_context->uc_mcontext.regs[29];
    lr = u_error_context->uc_mcontext.regs[30];
    
    unsigned long long int pc[30], lr_s[30], fp_s[30];
    unsigned int fd_index[30];
    pc[0] = u_error_context->uc_mcontext.pc;// 错误现场的PC值
    lr_s[0] = lr;
    fp_s[0] = fp;
    unsigned stack_size = 1;

    do
    {
        asm volatile(
            "ldr %0,[%1,#8]\n"
            "ldr %1,[%1]\n"

            : "+r"(lr), "+r"(fp)
            : "r"(lr), "r"(fp)
            : "memory");

        lr_s[stack_size] = lr;
        fp_s[stack_size] = fp;
        pc[stack_size] = lr;
        stack_size++;
    } while (fp != 0 && stack_size < 30);

    unsigned long long int start;
    unsigned long long int end;
    {
        
        sprintf(buf, "/proc/%d/maps", getpid());

        FILE * fp_map = fopen(buf, "r");

        char str_get[1024];
        
        while (1)
        {
            fscanf(fp_map, "%s\n", str_get);
            if (feof(fp_map))
            {
                break;
            }
            if(strlen(str_get) == 21)
            {
                if(str_get[10] == '-')
                {
                    i = 0;
                    start = 0;
                    end = 0;
                    do
                    {
                        if ('a' <= str_get[i] && str_get[i] <= 'f')
                        {
                            start = start*16 + str_get[i]-'a'+10;
                        }
                        else
                        {
                            start = start*16 + str_get[i]-'0';
                        }

                        i++;
                    } while ('-' != str_get[i]);// 计算程序在内存中开头的值

                    i++;

                    do
                    {
                        if ('a' <= str_get[i] && str_get[i] <= 'f')
                        {
                            end = end*16 + str_get[i]-'a'+10;
                        }
                        else
                        {
                            end = end*16 + str_get[i]-'0';
                        }

                        i++;
                    } while (0 != str_get[i]);// 计算程序在内存中结尾的值
                }
            }
            if(str_get[0] == '/')
            {
                int read_elf_flag = 0;
                i = 0;
                do
                {
                    if (start <= pc[i] && pc[i] < end)
                    {
                        pc[i] = pc[i] - start;
                        fd_index[i] = fd_num;
                        read_elf_flag = 1;
                    }
                    i++;
                } while (i != stack_size);// 计算出函数地址

                if(read_elf_flag == 1)
                {
                    printf("fd:%d-%s\n", fd_num, str_get);
                    fd=open(str_get, O_RDONLY);
                    getElfHeader();
                    getSectionHeaders();
                    getSections();
                    fd_num++;
                    close(fd);
                }
            }
        }
    }
    
    i = 0;
    do
    {
        printf("fd:%d-lr:%llx-fp:%llx-pc:%llx\n", fd_index[i], lr_s[i], fp_s[i], pc[i]);
        prtSymbols(pc[i], fd_index[i]);
        i++;
    } while (i != stack_size);
    
    
    printf("------show stack(my)------\n");

    siglongjmp(env, 1);
    // exit(0);// 不退出的话需要有信号变化
}

void RegSig()
{
    struct sigaction act;
    act.sa_sigaction = SigSegvProc; //处理函数
    act.sa_flags = SA_SIGINFO;      //如果设置了SA_SIGINFO标志位，则会使用sa_sigaction处理函数，否则使用sa_handler处理函数
    sigaddset(&act.sa_mask, SIGSEGV);
    sigaction(SIGSEGV, &act, NULL);
}

int func2()
{
    int *a = NULL;
    *a = 1;
    printf("func2 error\n");
    return 0;
}

int func1()
{
    printf("func2 %p\n", (void *)func2);
    func2();
    return 0;
}

int main(int argc, char **argv)
{
    RegSig();

    pid_t pid;
    if ((pid = fork()) == 0)
    {
        int r = sigsetjmp(env,1);
        if (r == 0)
        {
            printf("son work\n");
            printf("func1 %p\n", (void *)func1);
            func1();
        }
        else
        {
            printf("jump this code\n");
        }
    }

    int i = 0;
    while (i != 2)
    {
        printf("father work\n");
        sleep(2);
        i++;
    }

    waitpid(pid, NULL, WNOHANG);

    return 0;
}
